import abc
import threading
import traceback
from collections import OrderedDict
from concurrent.futures import thread
from concurrent.futures.thread import ThreadPoolExecutor
from enum import Enum
from typing import Union, List, Optional

from emi.core.Context import Context
from emi.core.Runner import RunnerContext, StopRunException

TASK_FINISEHD_EVENT = "_TASK_FINISHED_EVENT_"

class TaskStatus(Enum):

    Ready = "ready"
    Running = "run"
    Error = "error"
    Ready_Run = "ready_run"
    Waiting = "wait" ## 等待执行
    STOP = "stop"  ## 已停止
    STOPING = "stopping"  ## 正在停止
    FINISH = "finish"

    def is_run(self):
        return self == TaskStatus.Running or self == TaskStatus.Waiting or self == TaskStatus.Ready_Run

_T_LOCK = threading.RLock()

class Task:
    """
    任务对象。
    + 方便管理，方便日志查看。
    + 方便扩展。
    + 追踪运行情况： 日志以及结果。
    """
    _ID_GEN = 0

    @abc.abstractmethod
    def get_name(self):
        raise  NotImplementedError()

    @abc.abstractmethod
    def _run(self,context:RunnerContext):
        raise NotImplementedError()

    @property
    def id(self) -> str:
        _id = getattr(self, "_id", None)
        if _id is None:
            with _T_LOCK:
                Task._ID_GEN+=1
                _id = f"default_{Task._ID_GEN}"
                setattr(self,"_id",_id)
        return _id

    @property
    def status(self)->TaskStatus:
        return getattr(self,"_status",TaskStatus.Ready)

    @property
    def progress(self):
        """
        0 - 1 的值
        """
        return getattr(self,"_progress",0.0)

    @progress.setter
    def progress(self,p:float):
        """
        0 - 1 的值
        """
        if p > 1:
            p = 1
        setattr(self,"_progress",p)
        _manager = getattr(self, "_manager", None)
        if _manager:
            _manager._on_task_process_changed(self)


class TaskManager:
    """
    后台任务管理器。
    """

    def __init__(self, context: Context):
        self._context = context
        self._register_tasks = OrderedDict()
        self._lock = threading.RLock()
        self._id_token = 0
        self._thread_pool = ThreadPoolExecutor(max_workers=20)
        self.__task_changed_listener = []

    def add_task_changed_listener(self,ls):
        """
        增加任务的监听
        ls.on_task_changed(task)
        :param ls:
        :return:
        """
        with self._lock:
            if ls in self.__task_changed_listener:
                return
            self.__task_changed_listener.append(ls)

    def remove_task_changed_listener(self,ls):
        """
        删除任务的监听
        :param ls:
        :return:
        """
        with self._lock:
            self.__task_changed_listener.remove(ls)

    def _on_task_process_changed(self,task:Task):
        old_p = task._old_progress
        if task.progress - old_p > 0.005:
            ## 避免过于频繁
            task._old_progress = task.progress
            ##print(f"_on_task_process_changed: name={task.get_name()},p={task.progress},listeners={len(self.__task_changed_listener)})")
            with self._lock:
                listeners = list(self.__task_changed_listener)
            for ls in listeners:
                ls.on_task_changed(task)

    def _on_task_status_changed(self,task:Task):
        ##print(f"_on_task_status_changed: name={task.get_name()},status={task.status},listeners={len(self.__task_changed_listener)})")
        with self._lock:
            listeners = list(self.__task_changed_listener)
        for ls in listeners:
            ls.on_task_changed(task)

    def register_task(self,task:Task):
        with self._lock:
            if self._register_tasks.get(task.id) is not None:
                raise RuntimeError(f"Has Already task name : {task.get_name()}" )
            self._id_token +=1
            setattr(task, "_id", self._id_token)
            self._register_tasks[task.id] = task

    def has_register(self,task:Task)->bool:
        return self.get_task_by_id(task.id) is not None

    def get_task_list(self)->List[Task]:
        """
        返回已经注册了的task列表
        :return:
        """
        return list(self._register_tasks.values())

    def get_task_by_id(self,id)->Optional[Task]:
        """
        返回已经注册了的task
        :return:
        """
        for task in self._register_tasks.values():
            if task.id == id:
                return task
        return None

    def _set_task_status(self,task:Task,status:TaskStatus):
        setattr(task, "_status", status)
        ###try:
        self._on_task_status_changed(task)
        # except Exception as e:
        #     raise NotImplementedError()

    def run_in_thread(self,fun,*args, **kwargs):
        """
        提交任务在后台线程执行。不可以控制的后台任务。
        :param fun:
        :return:
        """
        self._thread_pool.submit(fun, *args,**kwargs)

    def run_task(self,task_or_task_id:Union[Task,str],in_thread:bool = True,on_finish_in_loop = None):
        """
        执行task任务。执行之前确保 task已经register_task到列表里面了。
        :param task_or_task_name: 传name值时，必须时注册到表里面的。
        :param in_thread: 是否在后台线程执行
        :param on_finish_in_loop : 完成时的回调函数，将在Looper单一线程中执行。
        :return:
        """
        if isinstance(task_or_task_id,Task):
            task = task_or_task_id
        else:
            task = self._register_tasks.get(task_or_task_id)
            if self._register_tasks.get(task.id) is None:
                raise RuntimeError(f"task 还没注册到任务列表里面: {task.get_name()}")

        with self._lock:
            if task.status == TaskStatus.Waiting or task.status == TaskStatus.Running:
                return
            feature = getattr(task,"_task_feature",None)
            if feature is not None:
                raise RuntimeError("task 已经注册在线程池里了")
            task.progress = 0.0
            task._old_progress = 0.0
            params = (task, on_finish_in_loop)
            if in_thread:
                ### 后台线程执行
                feature = self._thread_pool.submit(self._run_task_in_thread, *params)
                setattr(task, "_task_feature", feature)
                self._set_task_status(task, TaskStatus.Waiting)
                assert task.status == TaskStatus.Waiting
            else:
                ### 非后台线程执行
                feature = object()
                setattr(task, "_task_feature", feature)
                self._set_task_status(task, TaskStatus.Waiting)
                self._run_task_in_thread(*params)
                assert task.status == TaskStatus.FINISH


    def stop(self,task:Task):
        with self._lock:
            if task.status == TaskStatus.STOPING:
                return
            feature = getattr(task,"_task_feature",None)
            if feature is None:
                ### 本身就没有启动
                return
            if feature.cancel():
                # cancel方法用于取消某个任务,该任务没有放入线程池中才能取消成功
                self._set_task_status(task, TaskStatus.STOP)
                self._reset_task(task)
            else:
                self._set_task_status(task, TaskStatus.STOPING)


    def _run_task_in_thread(self,task:Task,on_finish_in_loop = None):
        with self._lock:
            self._set_task_status(task,TaskStatus.Running)
            setattr(task, "_manager", self)
        task_content = TaskContext(self._context,task)
        try:
            task._run(task_content)
        except StopRunException:
            pass
        except Exception as e:
            with self._lock:
                self._set_task_status(task, TaskStatus.Error)
                self._reset_task(task)
            traceback.print_exc()
            return
        with self._lock:
            old_status = task.status
            if old_status == TaskStatus.STOPING:
                self._set_task_status(task, TaskStatus.STOP)
            else:
                self._set_task_status(task, TaskStatus.FINISH)
            self._reset_task(task)

        if task.status == TaskStatus.FINISH:
            if on_finish_in_loop:
                def _call_back_finished(**args):
                    on_finish_in_loop()
                self._context.post(_call_back_finished,args={})

            self._context.post_event(TASK_FINISEHD_EVENT,task)



    def _reset_task(self,task:Task):
        setattr(task, "_task_feature", None)
        setattr(task, "_manager", None)



class TaskContext(RunnerContext):

    """
    运行环境。
    """
    def __init__(self, context:Context,task:Task):
        ##主线程环境
        super().__init__(context,None)
        self._task = task

    def is_stop(self):
        status = self._task.status
        is_runing = status == TaskStatus.Running or  status == TaskStatus.Waiting
        return  not is_runing

    def get_progress(self):
        return self._task.progress

    def set_progress(self, p: float):
        new_p = self._range_start + p * (self._range_end - self._range_start) / 1.0
        self._task.progress = new_p